package socketTest;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Client_New {

  private static final int HEAD_LEN = 24;
  private static final int RESERVED_CAPACITY = 100;
  private static final int SERVER_PORT = 41111;
  private static final int ENCRYPT = 0;
  private static final int DECRYPT = 1;
  private static final int MAX_SEND_BUFF_SIZE = 3 * 1024 * 1024;
  private static final Integer SUCESS = Integer.valueOf(0 + "");
  private static final Integer FAILED = Integer.valueOf(1 + "");
  private static Socket m_socket;
  private static int m_keyLen = 16;
  private static int m_colID = 0;
  private static int m_action = ENCRYPT;
  private static String m_errorMsg = null;
  private static String m_IP = null;
  private static StringBuffer m_data = null;
  private static int m_currSendDataSize = HEAD_LEN + 4;
  private static List m_rowIDList = null;
  private static List m_dataLenList = null;
  private static Map m_resultMap = null;

  public static void main(String[] args) {
    m_IP = "10.1.1.211";
    /***
     * session_key:column_id,plaintext
     */
    /***
     System.out.println(Client_New.Encrypt(Integer.valueOf("921"), "7777", 1));
     System.out.println(Client_New.Decrypt("9903000098736FC1C32617E143C0B17F7CCBF7EDC1130000", 1));

     System.out.println(Client_New.Encrypt(Integer.valueOf("921"), "8888",1));
     System.out.println(Client_New.Decrypt("990300007572718B114F10376A1427D670A9637CC1130000",1));
     System.out.println("===============================");
     System.out.println(Client_New.Encrypt(Integer.valueOf("4943"), "硬加密行加密", 3));
     System.out.println("===============================");
     ***/
    System.out.println(Client_New.Encrypt(Integer.valueOf("41"), "8888", 1));
    System.out.println(Client_New.Decrypt("34A4743ABFB379CE6F12C5A948277AB039000000", 1));

    System.out.println(Client_New.Encrypt(Integer.valueOf("107"), "硬加密列加密", 1));
  }

  private static void log(String par) {
    File f = null;
    if (System.getProperty("os.name").toLowerCase().indexOf("windows") > -1) {
      f = new File("c://oracle/test.txt");
    } else {
      new File("/home/oracle/test.txt");
    }
    OutputStream os = null;
    try {
      if (!f.exists()) {
        f.createNewFile();
      }
      os = new FileOutputStream(f, true);
      byte[] data = par.getBytes();
      os.write(data, 0, data.length);
      os.write("  ".getBytes());
      os.flush();
      os.close();
    } catch (Exception e) {
      if (os != null) {
        try {
          os.close();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    }
  }

  private static boolean SendData(byte[] data) {
    boolean result = false;
    try {
      if (null == m_IP) {
        SetErrorMsg("[SendData]: server ip is unvalid");
        return false;
      }
      if (null == m_socket || m_socket.isClosed()) {
        m_socket = new Socket(m_IP, SERVER_PORT);
      }
      DataOutputStream out = new DataOutputStream(m_socket.getOutputStream());
      out.write(data, 0, data.length);
      result = true;
    } catch (Exception e) {
      SetErrorMsg("[SendData]: write data failed");
      log("SendData error:" + e.getMessage());
      e.printStackTrace();
    }
    return result;
  }

  private static byte[] RecvData(int revLen) {
    try {
      if (null == m_IP) {
        SetErrorMsg("[RecvData]: server ip is unvalid");
        return null;
      }
      if (null == m_socket || m_socket.isClosed()) {
        m_socket = new Socket(m_IP, SERVER_PORT);
      }
      DataInputStream in = new DataInputStream(m_socket.getInputStream());
      byte[] data = new byte[revLen];
      int count = 0;
      int startPos = 0;
      int validLen = HEAD_LEN;
      int needReadLen = 0;
      boolean bfirstTime = true;
      while (validLen > 0 & (count = in.read(data, startPos, validLen)) > 0) {
        if (bfirstTime & count >= HEAD_LEN) {
          int extLen = (data[11] & 0xFF) << 24 | (data[10] & 0xFF) << 16 | (data[9] & 0xFF) << 8 | (data[8] & 0xFF);
          if (0 == extLen) {
            SetErrorMsg("[RecvData]: invalid extend data len");
            return null;
          }
          needReadLen = extLen + HEAD_LEN;
          validLen = needReadLen;
          bfirstTime = false;
        }
        validLen -= count;
        startPos += count;
      }
      if (startPos == needReadLen) {
        return data;
      }
    } catch (Exception e) {
      SetErrorMsg("[RecvData]: read data failed");
      e.printStackTrace();
    }
    return null;
  }

  private static void Init() {
    m_currSendDataSize = HEAD_LEN + 4;
    if (null != m_data) {
      m_data.delete(0, m_data.length());
    } else {
      m_data = new StringBuffer(RESERVED_CAPACITY);
    }
    if (null != m_dataLenList) {
      m_dataLenList.clear();
    } else {
      m_dataLenList = new ArrayList(RESERVED_CAPACITY);
    }
    if (null != m_rowIDList) {
      m_rowIDList.clear();
    } else {
      m_rowIDList = new ArrayList(RESERVED_CAPACITY);
    }
    if (null != m_resultMap) {
      m_resultMap.clear();
    } else {
      m_resultMap = new HashMap();
    }
  }

  private static byte[] MakeSendDataForBathProcess(Integer action, Integer colID) {
    int rawDataLen = 0;
    try {
      if (ENCRYPT == action.intValue()) {
        rawDataLen = m_data.toString().getBytes("utf-8").length;
      } else {
        rawDataLen = m_data.length();
      }
    } catch (Exception e) {
      SetErrorMsg("[MakeSendDataForBathProcess]: get byte length failed");
      return null;
    }
    int count = m_dataLenList.size();
    int extDataLen = 4 + 4 * count;
    extDataLen += (action.intValue() == ENCRYPT ? rawDataLen : rawDataLen / 2);
    byte[] sendData = new byte[extDataLen + HEAD_LEN];
    //Head
    sendData[0] = (byte) (action.intValue() == ENCRYPT ? 0xa1 : 0xa2);
    sendData[1] = (byte) 1;
    sendData[2] = (byte) 0;
    sendData[3] = (byte) 0;
    sendData[4] = (byte) (colID.intValue() & 0xFF);
    sendData[5] = (byte) ((colID.intValue() >> 8) & 0xFF);
    sendData[6] = (byte) ((colID.intValue() >> 16) & 0xFF);
    sendData[7] = (byte) ((colID.intValue() >> 24) & 0xFF);
    sendData[8] = (byte) (extDataLen & 0xFF);
    sendData[9] = (byte) ((extDataLen >> 8) & 0xFF);
    sendData[10] = (byte) ((extDataLen >> 16) & 0xFF);
    sendData[11] = (byte) ((extDataLen >> 24) & 0xFF);
    //Following is ExtData
    int startPos = HEAD_LEN;
    //Count
    sendData[startPos++] = (byte) (count & 0xFF);
    sendData[startPos++] = (byte) ((count >> 8) & 0xFF);
    sendData[startPos++] = (byte) ((count >> 16) & 0xFF);
    sendData[startPos++] = (byte) ((count >> 24) & 0xFF);
    //Raw Data Length
    int denominator = (action.intValue() == ENCRYPT ? 1 : 2);
    for (int i = 0; i < count; ++i) {
      int dataLen = Integer.parseInt(m_dataLenList.get(i) + "") / denominator;
      sendData[startPos++] = (byte) (dataLen & 0xFF);
      sendData[startPos++] = (byte) ((dataLen >> 8) & 0xFF);
      sendData[startPos++] = (byte) ((dataLen >> 16) & 0xFF);
      sendData[startPos++] = (byte) ((dataLen >> 24) & 0xFF);
    }
    //Raw Data
    if (ENCRYPT == action.intValue()) {
      try {
        System.arraycopy(m_data.toString().getBytes("utf-8"), 0, sendData, startPos, rawDataLen);
      } catch (Exception e) {
        SetErrorMsg("[MakeSendDataForBathProcess]: array copy failed");
        return null;
      }
    } else {
      for (int i = 0; i < m_data.length() - 4; i += 4) {
        int val = Integer.parseInt(m_data.toString().substring(i, i + 4), 16);
        sendData[startPos++] = (byte) ((val >> 8) & 0xFF);
        sendData[startPos++] = (byte) (val & 0xFF);
      }
    }
    return sendData;
  }

  private static byte[] getSendDataForEncrypt(Integer uniqueID, String plaintext) {
    int plaintextLen = 0;//明文数据长度
    int extDataLen = 0;//扩展数据长度
    int sendSize = 0;//发送数据长度
    byte sendBuffer[] = null;//发送数据包
    int startPos = HEAD_LEN;//扩展数据起始位置
    /*开始计算明文数据长度*/
    try {
      plaintextLen = plaintext.getBytes("utf-8").length;
    } catch (Exception e) {
      SetErrorMsg("[getSendDataForEncrypt]: get bytes length failed");
      e.printStackTrace();
      return null;
    }
    /*结束计算明文数据长度*/
    /*开始计算扩展数据长度*/
    //4字节【分组个数】+4字节【一组明文长度】+n字节【明文数据】
    extDataLen = plaintextLen + 4 + 4;//默认列模式，目前长度写死，都即只传一组数据
    /*结束计算扩展数据长度*/
    sendSize = HEAD_LEN + extDataLen;//头部+扩展数据长度
    sendBuffer = new byte[sendSize];//发送包
    /*开始填充发送包*/
    sendBuffer[0] = (byte) 0xa1;
    sendBuffer[1] = (byte) 1;
    sendBuffer[2] = (byte) 0;
    sendBuffer[3] = (byte) 0;
    sendBuffer[4] = (byte) (uniqueID.intValue() & 0xFF);
    sendBuffer[5] = (byte) ((uniqueID.intValue() >> 8) & 0xFF);
    sendBuffer[6] = (byte) ((uniqueID.intValue() >> 16) & 0xFF);
    sendBuffer[7] = (byte) ((uniqueID.intValue() >> 24) & 0xFF);
    sendBuffer[8] = (byte) (extDataLen & 0xFF);
    sendBuffer[9] = (byte) ((extDataLen >> 8) & 0xFF);
    sendBuffer[10] = (byte) ((extDataLen >> 16) & 0xFF);
    sendBuffer[11] = (byte) ((extDataLen >> 24) & 0xFF);
    //Count
    sendBuffer[startPos++] = (byte) 1;
    sendBuffer[startPos++] = (byte) 0;
    sendBuffer[startPos++] = (byte) 0;
    sendBuffer[startPos++] = (byte) 0;
    //DataSize
    sendBuffer[startPos++] = (byte) (plaintextLen & 0xFF);
    sendBuffer[startPos++] = (byte) ((plaintextLen >> 8) & 0xFF);
    sendBuffer[startPos++] = (byte) ((plaintextLen >> 16) & 0xFF);
    sendBuffer[startPos++] = (byte) ((plaintextLen >> 24) & 0xFF);
    /*结束填充发送包*/
    /*开始填充明文数据到发送包*/
    try {
      System.arraycopy(plaintext.getBytes("utf-8"), 0, sendBuffer, startPos, plaintextLen);
    } catch (Exception e) {
      SetErrorMsg("[getSendDataForEncrypt]: array copy failed");
      return null;
    }
    /*结束填充明文数据到发送包*/
    return sendBuffer;
  }

  private static byte[] getSendDataForEncrypt(Long uniqueID, String plaintext, Integer encMode) {
    int plaintextLen = 0;//明文数据长度
    int extDataLen = 0;//扩展数据长度
    int sendSize = 0;//发送数据长度
    byte sendBuffer[] = null;//发送数据包
    int startPos = HEAD_LEN;//扩展数据起始位置
    /*开始计算明文数据长度*/
    try {
      plaintextLen = plaintext.getBytes("utf-8").length;
    } catch (Exception e) {
      SetErrorMsg("[getSendDataForEncrypt]: get bytes length failed");
      e.printStackTrace();
      return null;
    }
    /*结束计算明文数据长度*/
    /*开始计算扩展数据长度*/
    switch (encMode) {
      case 3:
        //4字节【分组个数】+4字节【一组明文长度】+8字节【一组密钥ID】+4字节【数据总长度】+n字节【明文数据】
        extDataLen = plaintextLen + 4 + 4 + 8 + 4;
        break;
      case 1:
      default:
        //4字节【分组个数】+4字节【一组明文长度】+n字节【明文数据】
        extDataLen = plaintextLen + 4 + 4;//默认列模式，目前长度写死，都即只传一组数据
        break;
    }
    /*结束计算扩展数据长度*/
    sendSize = HEAD_LEN + extDataLen;//头部+扩展数据长度
    sendBuffer = new byte[sendSize];//发送包
    /*开始填充发送包*/
    switch (encMode) {
      case 3:
        sendBuffer[0] = (byte) 0xa4;
        sendBuffer[1] = (byte) 1;
        sendBuffer[2] = (byte) 0;
        sendBuffer[3] = (byte) 0;
        sendBuffer[4] = (byte) 0;
        sendBuffer[5] = (byte) 0;
        sendBuffer[6] = (byte) 0;
        sendBuffer[7] = (byte) 0;
        sendBuffer[8] = (byte) (extDataLen & 0xFF);
        sendBuffer[9] = (byte) ((extDataLen >> 8) & 0xFF);
        sendBuffer[10] = (byte) ((extDataLen >> 16) & 0xFF);
        sendBuffer[11] = (byte) ((extDataLen >> 24) & 0xFF);
        //组的个数，目前写死为：1组
        sendBuffer[startPos++] = (byte) 1;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        //每组数据的长度，目前写死为1组，则为1组数据长度
        sendBuffer[startPos++] = (byte) (plaintextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 24) & 0xFF);
        //每组密钥ID，目前写死为1组，则为1组密钥ID
        sendBuffer[startPos++] = (byte) (uniqueID & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 24) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 32) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 40) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 48) & 0xFF);
        sendBuffer[startPos++] = (byte) ((uniqueID >> 56) & 0xFF);
        //数据总长度，目前写死为1组，则为1组数据长度，批量时这里的长度就是所有组的数据长度总和
        sendBuffer[startPos++] = (byte) (plaintextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 24) & 0xFF);
        break;
      case 1:
      default:
        sendBuffer[0] = (byte) 0xa1;
        sendBuffer[1] = (byte) 1;
        sendBuffer[2] = (byte) 0;
        sendBuffer[3] = (byte) 0;
        sendBuffer[4] = (byte) (uniqueID.intValue() & 0xFF);
        sendBuffer[5] = (byte) ((uniqueID.intValue() >> 8) & 0xFF);
        sendBuffer[6] = (byte) ((uniqueID.intValue() >> 16) & 0xFF);
        sendBuffer[7] = (byte) ((uniqueID.intValue() >> 24) & 0xFF);
        sendBuffer[8] = (byte) (extDataLen & 0xFF);
        sendBuffer[9] = (byte) ((extDataLen >> 8) & 0xFF);
        sendBuffer[10] = (byte) ((extDataLen >> 16) & 0xFF);
        sendBuffer[11] = (byte) ((extDataLen >> 24) & 0xFF);
        //Count
        sendBuffer[startPos++] = (byte) 1;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        //DataSize
        sendBuffer[startPos++] = (byte) (plaintextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((plaintextLen >> 24) & 0xFF);
        break;
    }
    /*结束填充发送包*/
    /*开始填充明文数据到发送包*/
    try {
      System.arraycopy(plaintext.getBytes("utf-8"), 0, sendBuffer, startPos, plaintextLen);
    } catch (Exception e) {
      SetErrorMsg("[getSendDataForEncrypt]: array copy failed");
      return null;
    }
    /*结束填充明文数据到发送包*/
    return sendBuffer;
  }

  private static byte[] getSendDataForDecrypt(String data, Integer encMode) {
    /*开始初始化数据*/
    Long keyID = (long) 0;//密钥ID
    int ciphertextLen = 0;//密文长度
    int extDataLen = 0;//扩展数据大小
    int sendSize = 0;//发送数据包大小
    byte sendBuffer[] = null;//发送包
    int startPos = HEAD_LEN;//扩展数据起始位置
    /*结束初始化数据*/
    /*开始计算密钥ID*/
    switch (encMode) {
      case 3:
        /*开始解析keyID*/
        StringBuilder keyIDStr = new StringBuilder();
        /***
         * ABCDEFGH
         * GH->GHEF->GHEFCD->GHEFCDAB
         */
        char[] keyIDChar = data.substring(0, 8).toCharArray();
        for (int i = keyIDChar.length - 1; i > 0; i -= 2) {
          keyIDStr.append(keyIDChar[i - 1]);
          keyIDStr.append(keyIDChar[i]);
        }
        /***
         * 16进制还原为10进制
         */
        keyID = Long.parseLong(keyIDStr.toString(), 16);
        /*结束解析keyID*/
        //密文数据，从第九位开始截取，直到末尾
        data = data.substring(8);
        //密文是4位转16进制存char中，现在要放在8位的byte中，所以长度减半
        //原来是16进制存储的，1个字节2个16，现在还原为字节，需要减半
        ciphertextLen = data.length() / 2;
        //4字节【分组个数】+4字节【一组明文长度】+8字节【一组密钥ID】+4字节【数据总长度】+n字节【明文数据】
        extDataLen = 4 + 4 + 8 + 4 + ciphertextLen;
        break;
      case 1:
      default:
        //密文是4位转16进制存char中，现在要放在8位的byte中，所以长度减半
        ciphertextLen = data.length() / 2;
        //4字节【分组个数】+4字节【一组明文长度】+n字节【明文数据】
        extDataLen = ciphertextLen + 4 + 4;//默认列模式，目前长度写死，都即只传一组数据
        break;
    }
    /*结束计算密钥ID*/
    /***
     * 总包的长度：  header+extData
     */
    sendSize = HEAD_LEN + extDataLen;//头部长度+扩展长度
    sendBuffer = new byte[sendSize];//发送包
    /*开始填充发送包*/
    switch (encMode) {
      case 3:
        //头部
        sendBuffer[0] = (byte) 0xa5;
        //版本号
        sendBuffer[1] = (byte) 1;
        //保留位
        sendBuffer[2] = (byte) 0;
        sendBuffer[3] = (byte) 0;
        sendBuffer[4] = (byte) 0;
        sendBuffer[5] = (byte) 0;
        sendBuffer[6] = (byte) 0;
        sendBuffer[7] = (byte) 0;
        //扩展数据长度
        sendBuffer[8] = (byte) (extDataLen & 0xFF);
        sendBuffer[9] = (byte) ((extDataLen >> 8) & 0xFF);
        sendBuffer[10] = (byte) ((extDataLen >> 16) & 0xFF);
        sendBuffer[11] = (byte) ((extDataLen >> 24) & 0xFF);
        //组个数
        sendBuffer[startPos++] = (byte) 1;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        //每组数据长度
        sendBuffer[startPos++] = (byte) (ciphertextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 24) & 0xFF);
        //每组密钥ID，目前写死为1组，则为1组密钥ID
        sendBuffer[startPos++] = (byte) (keyID & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 24) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 32) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 40) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 48) & 0xFF);
        sendBuffer[startPos++] = (byte) ((keyID >> 56) & 0xFF);
        //每组数据长度
        sendBuffer[startPos++] = (byte) (ciphertextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 24) & 0xFF);
        for (int i = 0; i < data.length() - 4; i += 4) {
          int val = Integer.parseInt(data.substring(i, i + 4), 16);
          sendBuffer[startPos++] = (byte) ((val >> 8) & 0xFF);
          sendBuffer[startPos++] = (byte) (val & 0xFF);
        }
        break;
      case 1:
      default:
        sendBuffer[0] = (byte) 0xa2;
        sendBuffer[1] = (byte) 1;
        sendBuffer[2] = (byte) 0;
        sendBuffer[3] = (byte) 0;
        sendBuffer[4] = (byte) 0;
        sendBuffer[5] = (byte) 0;
        sendBuffer[6] = (byte) 0;
        sendBuffer[7] = (byte) 0;
        sendBuffer[8] = (byte) (extDataLen & 0xFF);
        sendBuffer[9] = (byte) ((extDataLen >> 8) & 0xFF);
        sendBuffer[10] = (byte) ((extDataLen >> 16) & 0xFF);
        sendBuffer[11] = (byte) ((extDataLen >> 24) & 0xFF);
        //Count
        sendBuffer[startPos++] = (byte) 1;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        sendBuffer[startPos++] = (byte) 0;
        //DataSize
        sendBuffer[startPos++] = (byte) (ciphertextLen & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 8) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 16) & 0xFF);
        sendBuffer[startPos++] = (byte) ((ciphertextLen >> 24) & 0xFF);
        for (int i = 0; i < data.length() - 4; i += 4) {
          int val = Integer.parseInt(data.substring(i, i + 4), 16);
          sendBuffer[startPos++] = (byte) ((val >> 8) & 0xFF);
          sendBuffer[startPos++] = (byte) (val & 0xFF);
        }
        break;
    }
    /*结束填充发送包*/
    return sendBuffer;
  }

  private static Integer GetCiphertext(byte[] response) {
    char[] hexArray = "0123456789ABCDEF".toCharArray();
    int count = (response[HEAD_LEN] & 0xFF) | ((response[HEAD_LEN + 1] & 0xFF) << 8) | ((response[HEAD_LEN + 2] & 0xFF) << 16) | (
        (response[HEAD_LEN + 3] & 0xFF) << 24);
    int dataLenPos = HEAD_LEN + 4;
    int ciphertextPos = dataLenPos + count * 4;
    for (int i = 0; i < count; ++i) {
      int offset = dataLenPos + 4 * i;
      int dataLen =
          (response[offset + 3] & 0xFF) << 24 | (response[offset + 2] & 0xFF) << 16 | (response[offset + 1] & 0xFF) << 8 | (response[offset]
              & 0xFF);
      if (0 == dataLen) {
        continue;
      }
      char[] hexChars = new char[dataLen * 2 + 4 * 2];
      //Add coldID On Ciphertext
      int index = 0;
      for (int j = 0; j < 4; ++j) {
        int val = (m_colID >> (8 * j)) & 0xFF;
        hexChars[index++] = hexArray[val >>> 4];
        hexChars[index++] = hexArray[val & 0x0F];
      }
      for (int k = 0; k < dataLen; ++k) {
        int val = response[k + ciphertextPos] & 0xFF;
        hexChars[index++] = hexArray[val >>> 4];
        hexChars[index++] = hexArray[val & 0x0F];
      }
      ciphertextPos += dataLen;
      m_resultMap.put(m_rowIDList.get(i), new String(hexChars));
    }
    return SUCESS;
  }

  private static Integer GetPlaintext(byte[] response) {
    int count = (response[HEAD_LEN] & 0xFF) | ((response[HEAD_LEN + 1] & 0xFF) << 8) | ((response[HEAD_LEN + 2] & 0xFF) << 16) | (
        (response[HEAD_LEN + 3] & 0xFF) << 24);
    int dataLenPos = HEAD_LEN + 4;
    int plaintextPos = dataLenPos + count * 4;
    try {
      for (int i = 0; i < count; ++i) {
        int offset = dataLenPos + 4 * i;
        int dataLen = (response[offset + 3] & 0xFF) << 24 | (response[offset + 2] & 0xFF) << 16 | (response[offset + 1] & 0xFF) << 8 | (
            response[offset] & 0xFF);
        if (0 == dataLen) {
          continue;
        }
        byte[] decryptData = new byte[dataLen];
        System.arraycopy(response, plaintextPos, decryptData, 0, dataLen);
        plaintextPos += dataLen;
        m_resultMap.put(m_rowIDList.get(i), new String(decryptData, "utf-8"));
      }
    } catch (UnsupportedEncodingException e) {
      SetErrorMsg("[GetPlaintext]: get plaintext failed");
      e.printStackTrace();
      return FAILED;
    }
    return SUCESS;
  }

  private static String RemoveColIDFromCipherText(String ciphertext) {
    if (ciphertext.length() <= 8) {
      return null;
    }
    return ciphertext.substring(8);
  }

  private static void SetErrorMsg(String errorMsg) {
    m_errorMsg = errorMsg;
  }

  private static Integer Do(Integer action, Integer colID) {
    byte[] sendData = MakeSendDataForBathProcess(action, colID);
    if (null == sendData) {
      return FAILED;
    }
    if (null != m_dataLenList) {
      m_dataLenList.clear();
    } else {
      m_dataLenList = new ArrayList(RESERVED_CAPACITY);
    }
    if (null != m_data) {
      m_data.delete(0, m_data.length());
    } else {
      m_data = new StringBuffer(RESERVED_CAPACITY);
    }
    if (false == SendData(sendData)) {
      return FAILED;
    }
    byte[] response = RecvData(m_currSendDataSize);
    if (null == response) {
      return FAILED;
    }
    Integer ret = (ENCRYPT == action.intValue() ? GetCiphertext(response) : GetPlaintext(response));
    if (null != m_rowIDList) {
      m_rowIDList.clear();
    } else {
      m_rowIDList = new ArrayList(RESERVED_CAPACITY);
    }
    return ret;
  }

  public static Integer SetServerIP(String IP) {
    if (null == IP || "" == IP) {
      SetErrorMsg("[SetServerIP]: IP is Unvalid");
      return FAILED;
    }
    m_IP = IP;
    return SUCESS;
  }

  public static Integer SetParams(Integer action, Integer colID, Integer keyLen) {
    if (null == action || null == colID || null == keyLen) {
      SetErrorMsg("[SetParams]: Parameters is illegal");
      return FAILED;
    }
    Init();
    m_action = action.intValue();
    m_colID = colID.intValue();
    m_keyLen = keyLen.intValue();
    return SUCESS;
  }

  public static Integer AddData(String rowID, String data) {
    if (null == rowID || null == data) {
      SetErrorMsg("[AddData]: rowID or data is unvalid");
      return FAILED;
    }
    if (m_action == DECRYPT) {
      data = RemoveColIDFromCipherText(data);
      if (null == data) {
        SetErrorMsg("[AddData]: Ciphertext is unvalid, data: " + data);
        return FAILED;
      }
    }
    int dataLen = 0;
    try {
      dataLen = data.getBytes("utf-8").length;
    } catch (Exception e) {
      e.printStackTrace();
      SetErrorMsg("[AddData]: getBytes Failed, data: " + data);
      return FAILED;
    }
    //For Encrypt, 4Bytes For KeyID
    int len = (m_action == ENCRYPT ? ((dataLen / m_keyLen + 1) * m_keyLen + 4) : dataLen / 2);
    len += 4; //result
    if (m_currSendDataSize + len > MAX_SEND_BUFF_SIZE) {
      Do(Integer.valueOf(m_action + ""), Integer.valueOf(m_colID + ""));
      m_currSendDataSize = HEAD_LEN + 4;
    }
    m_data.append(data);
    m_currSendDataSize += len;
    m_dataLenList.add(Integer.valueOf(dataLen + ""));
    m_rowIDList.add(rowID);
    return SUCESS;
  }

  public static Integer BatchProcess() {
    if (0 == m_data.length()) {
      return SUCESS;
    }
    Integer result = Do(Integer.valueOf(m_action + ""), Integer.valueOf(m_colID + ""));
    return result;
  }

  public static String GetResultByRowID(String rowID) {
    if (m_resultMap.containsKey(rowID)) {
      return (String) m_resultMap.get(rowID);
    }
    SetErrorMsg("[GetResultByRowID]: rowID is not exist or encrypt/decrypt failed, rowID: " + rowID);
    return null;
  }

  public static String Encrypt(Integer uniqueID, String plaintext, Integer encMode) {
    if (null == uniqueID || null == plaintext || 0 == plaintext.length()) {
      SetErrorMsg("[Encrypt]: colID or plaintext is unvalid");
      return null;
    }
    char[] hexArray = "0123456789ABCDEF".toCharArray();
    byte[] sendData = getSendDataForEncrypt(uniqueID.longValue(), plaintext, encMode);
    if (false == SendData(sendData)) {
      return null;
    }
    byte[] response = RecvData(sendData.length + 256);
    if (null == response) {
      return null;
    }
    int offset = HEAD_LEN + 4;
    int ciphertextLen =
        (response[offset + 3] & 0xFF) << 24 | (response[offset + 2] & 0xFF) << 16 | (response[offset + 1] & 0xFF) << 8 | (response[offset]
            & 0xFF);
    if (0 == ciphertextLen) {
      SetErrorMsg("[Encrypt]: ciphertext length is unvalid");
      return null;
    }
    int index = 0;
    char[] hexChars = new char[ciphertextLen * 2 + 4 * 2];
    for (int i = 0; i < 4; ++i) {
      int val = (uniqueID.intValue() >> (8 * i)) & 0xFF;
      hexChars[index++] = hexArray[val >>> 4];
      hexChars[index++] = hexArray[val & 0x0F];
    }
    for (int j = 0; j < ciphertextLen; ++j) {
      int val = response[j + offset + 4] & 0xFF;
      hexChars[index++] = hexArray[val >>> 4];
      hexChars[index++] = hexArray[val & 0x0F];
    }
    return new String(hexChars);
  }

  public static String Decrypt(String ciphertext, Integer encMode) {
    if (null == ciphertext || 0 == ciphertext.length()) {
      SetErrorMsg("[Decrypt]: cipheretxt is null");
      return null;
    }
    String rawCiphertext = null;
    switch (encMode) {
      case 3:
        rawCiphertext = ciphertext;
        break;
      case 1:
      default:
        rawCiphertext = RemoveColIDFromCipherText(ciphertext);
        if (null == rawCiphertext) {
          SetErrorMsg("[Decrypt]: ciphertext is illegal");
          return null;
        }
        break;
    }
    byte[] sendData = getSendDataForDecrypt(rawCiphertext, encMode);
    if (false == SendData(sendData)) {
      return null;
    }
    byte[] response = RecvData(sendData.length);
    if (null == response) {
      return null;
    }
    int offset = HEAD_LEN + 4;
    int plaintextLen =
        (response[offset + 3] & 0xFF) << 24 | (response[offset + 2] & 0xFF) << 16 | (response[offset + 1] & 0xFF) << 8 | (response[offset]
            & 0xFF);
    if (0 == plaintextLen) {
      SetErrorMsg("[Decrypt]: plaintext lenght is unvalid");
      return null;
    }
    String plaintext = null;
    try {
      byte[] decryptData = new byte[plaintextLen];
      System.arraycopy(response, offset + 4, decryptData, 0, plaintextLen);
      plaintext = new String(decryptData, "utf-8");
    } catch (UnsupportedEncodingException e) {
      SetErrorMsg("[Decrypt]: decrypt data tranform to string failed");
      e.printStackTrace();
    }
    return plaintext;
  }

  public static Integer Clear() {
    m_data = null;
    m_rowIDList = null;
    m_dataLenList = null;
    m_resultMap = null;
    System.gc();
    return Integer.valueOf(0 + "");
  }

  public static Integer DestroyResources() {
    Clear();
    Integer result = Integer.valueOf(1 + "");
    if (null != m_socket && !m_socket.isClosed()) {
      try {
        m_socket.close();
        m_socket = null;
        result = Integer.valueOf(0 + "");
      } catch (Exception e) {
        SetErrorMsg("[DestroyResources]: close socket failed");
        e.printStackTrace();
      }
    }
    SetErrorMsg("[DestroyResources]: socket is invalid");
    return result;
  }

  public static String GetLastErrorMsg() {
    return m_errorMsg;
  }
}
